using System;
using System.Collections.Generic;
using Rarous.NForms.Validation;
using System.Text;

namespace Rarous.NForms {
  /// <summary>
  /// Abstraktní třída formulářového prvku.
  /// </summary>
  public abstract class FormItem {
    protected const string LabelTag = "label";
    protected const string InputTag = "input";
    protected const string TextAreaTag = "textarea";
    protected const string SelectTag = "select";
    protected const string OptionTag = "option";
    protected const string ValueAttribute = "Value";

    protected IList<ValidationRule> validationRules = new List<ValidationRule>();
    private string value;

    /// <summary>
    /// Vrací HTML elememt formulářového prvku.
    /// </summary>
    public HtmlElement Control {
      get;
      protected set;
    }

    /// <summary>
    /// Vrací HTML element formulářového popisku.
    /// </summary>
    public HtmlElement Label {
      get;
      protected set;
    }

    /// <summary>
    /// Vrací nebo nastavuje nadřazený formulář.
    /// </summary>
    protected Form Parent {
      get;
      set;
    }

    /// <summary>
    /// Vrací nebo nastavuje název formulářvého pole.
    /// </summary>
    protected string Name {
      get;
      set;
    }

    /// <summary>
    /// Vrací hodnotu získanou z HTTP požadavku.
    /// </summary>
    protected internal string Value {
      get {
        if (String.IsNullOrEmpty(value)) {
          return String.Empty;
        }
        return value.Trim();
      }
      set {
        this.value = value;
      }
    }

    /// <summary>
    /// Vrací nebo nastavuje prázdnou hodnotu.
    /// </summary>
    protected object EmptyValue {
      get;
      set;
    }

    /// <summary>
    /// Vrací příznak, zda je hodnota formulářového prvku validní.
    /// </summary>
    public bool IsValid {
      get;
      private set;
    }

    public FormItem AddRule(Predicate<object> predicate, string message) {
      return AddRule(new PredicateRule(message, predicate));
    }

    public FormItem AddMinimalLengthRule(int minLength, string message) {
      return AddRule(new MinimalLengthRule(minLength, message));
    }

    public FormItem AddRule(ValidationRule rule) {
      validationRules.Add(rule);
      return this;
    }

    /// <summary>
    /// Zaregistruje formulářový prvek do formuláře.
    /// </summary>
    /// <param name="parent"></param>
    /// <param name="name"></param>
    protected void Register(Form parent, string name) {
      Parent = parent;
      Name = name;
      Parent[Name] = this;
    }

    /// <summary>
    /// Nastaví atributy.
    /// </summary>
    /// <param name="attributes">Objekt s atributy.</param>
    protected FormItem SetAttributes(object attributes) {
      if (attributes != null) {
        Control.LoadAtributes(attributes);
      }
      return this;
    }

    protected string ConvertNameToId(string name) {
      StringBuilder sb = new StringBuilder();
      bool capitalize = false;

      foreach (char @char in name) {
        if (@char == '_') {
          capitalize = true;
          continue;
        }

        if (capitalize) {
          sb.Append(Char.ToUpper(@char));
          capitalize = false;
        }
        else {
          sb.Append(@char);
        }
      }

      return sb.ToString();
    }

    /// <summary>
    /// Načte hodnotu a převede ji na požadovaný typ.
    /// </summary>
    /// <typeparam name="T">Typ, na který chceme hodnotu zkonvertovat.</typeparam>
    /// <param name="convert">Delegát na konvertovací funkci, která převede hodnotu na požadovaný typ.</param>
    /// <returns>Vrací hodnotu převedenou na danný typ. Pokud je hodnota shodná s prázdnou hodnotou, 
    /// vrací <c>default(T)</c>.</returns>
    public T GetValue<T>(Converter<string, T> convert) {
      var value = convert(Value);
      if (value.Equals(EmptyValue)) {
        return default(T);
      }
      return value;
    }

    /// <summary>
    /// Načte hodnotu jako řatězec.
    /// </summary>
    /// <returns>Vrací hodnotu jako řetězec. Pokud je hodnota shodná s prázdnou hodnotou, 
    /// vrací <c>String.Empty</c>.</returns>
    public string GetValue() {
      if (Value.Equals(EmptyValue)) {
      	return String.Empty;
      }
      return Value;
    }

    /// <summary>
    /// Nastavuje hodnotu formulářového prvku.
    /// </summary>
    /// <param name="value">Nastavovaná hodnota.</param>
    public virtual FormItem SetValue(object value) {
      SetAttribute(ValueAttribute, value);
      return this;
    }

    /// <summary>
    /// Nastavuje přednastavenou hodnotu formulářového prvku, která je brána za prázdnou.
    /// </summary>
    /// <param name="value">Hodnota považovaná za prázdnou.</param>
    public virtual FormItem SetEmptyValue(object value) {
      EmptyValue = value;
      SetValue(value);
      return this;
    }

    /// <summary>
    /// Nastaví hodnotu atributu.
    /// </summary>
    /// <param name="name">Název atributu.</param>
    /// <param name="value">Hodnota atributu.</param>
    public FormItem SetAttribute(string name, object value) {
      Control[name] = value;
      return this;
    }

    public virtual void Validate() {
      bool result = true;
      foreach (ValidationRule rule in validationRules) {
        bool ruleResult = rule.SetValue(GetValue()).Validate();
        result &= ruleResult;
        if (ruleResult == false) {
          Parent.Messages.Add(rule.ErrorMessage);
        }
      }
      IsValid = result;
    }

    protected HtmlElement CreateLabel(string name, string label) {
      return String.IsNullOrEmpty(label)
              ? new HtmlElement(String.Empty)
              : new HtmlElement(LabelTag, new { For = ConvertNameToId(name), Text = label });
    }

    protected static void ConstructorContract(Form parent, string name) {
      if (parent == null) {
        throw new ArgumentNullException("parent", "parent is null.");
      }
      if (String.IsNullOrEmpty(name)) {
        throw new ArgumentException("name should be set", "name");
      }
    }
  }
}
